#include "protocol/modbus/ProtocolManager.h"
#include "core/Logger.h"

#include <fstream>
#include <stdexcept>
#include <cstring>
#include <algorithm>

namespace ems {
namespace protocol {

ProtocolManager::ProtocolManager() {
}

ProtocolManager::~ProtocolManager() {
}

bool ProtocolManager::loadProtocol(const std::string& protocolFile) {
    try {
        std::ifstream file(protocolFile);
        if (!file.is_open()) {
            LOG_ERROR("Cannot open protocol file: " << protocolFile);
            return false;
        }
        
        utils::Json json;
        if (!json.parse(file)) {
            LOG_ERROR("Failed to parse protocol file: " << protocolFile);
            return false;
        }
        
        config_ = ProtocolConfig();
        dataPointIndex_.clear();
        
        if (!parseProtocolInfo(json) ||
            !parseConnection(json) ||
            !parseDataPoints(json) ||
            !parseFunctionCodes(json) ||
            !parseErrorCodes(json)) {
            return false;
        }
        
        for (size_t i = 0; i < config_.dataPoints.size(); ++i) {
            dataPointIndex_[config_.dataPoints[i].name] = i;
        }
        
        LOG_INFO("Successfully loaded protocol: " << config_.vendor << " " << config_.model 
                 << " with " << config_.dataPoints.size() << " data points");
        return true;
        
    } catch (const std::exception& e) {
        LOG_ERROR("Exception loading protocol: " << e.what());
        return false;
    }
}

bool ProtocolManager::parseProtocolInfo(const utils::Json& json) {
    auto protocolInfo = json.getObject("protocol_info");
    if (protocolInfo.isNull()) {
        LOG_ERROR("Missing protocol_info section");
        return false;
    }
    
    config_.vendor = protocolInfo.getString("vendor");
    config_.model = protocolInfo.getString("model");
    config_.version = protocolInfo.getString("version");
    config_.description = protocolInfo.getString("description");
    
    return true;
}

bool ProtocolManager::parseConnection(const utils::Json& json) {
    auto connection = json.getObject("connection");
    if (connection.isNull()) {
        LOG_WARN("Missing connection section, using defaults");
        return true;
    }
    
    config_.defaultSlaveId = connection.getInt("default_slave_id", 1);
    config_.defaultTimeout = connection.getInt("default_timeout", 3000);
    config_.retryTimes = connection.getInt("retry_times", 3);
    config_.retryInterval = connection.getInt("retry_interval", 1000);
    
    return true;
}

bool ProtocolManager::parseDataPoints(const utils::Json& json) {
    auto dataPointsArray = json.getArray("data_points");
    if (dataPointsArray.empty()) {
        LOG_ERROR("Missing or empty data_points array");
        return false;
    }
    
    config_.dataPoints.clear();
    config_.dataPoints.reserve(dataPointsArray.size());
    
    for (const auto& pointJson : dataPointsArray) {
        try {
            DataPoint dataPoint = parseDataPoint(pointJson);
            config_.dataPoints.push_back(dataPoint);
        } catch (const std::exception& e) {
            LOG_ERROR("Failed to parse data point: " << e.what());
            return false;
        }
    }
    
    return true;
}

bool ProtocolManager::parseFunctionCodes(const utils::Json& json) {
    auto functionCodes = json.getObject("function_codes");
    if (!functionCodes.isNull()) {
        auto codeMap = functionCodes.getMap();
        for (const auto& pair : codeMap) {
            config_.functionCodes[pair.first] = pair.second.asString();
        }
    }
    return true;
}

bool ProtocolManager::parseErrorCodes(const utils::Json& json) {
    auto errorCodes = json.getObject("error_codes");
    if (!errorCodes.isNull()) {
        auto codeMap = errorCodes.getMap();
        for (const auto& pair : codeMap) {
            config_.errorCodes[pair.first] = pair.second.asString();
        }
    }
    return true;
}

DataPoint ProtocolManager::parseDataPoint(const utils::Json& json) const {
    DataPoint dataPoint;

    // Basic information
    dataPoint.name = json.getString("name");
    dataPoint.chineseName = json.getString("chinese_name");
    dataPoint.address = static_cast<uint16_t>(json.getInt("address"));
    dataPoint.length = static_cast<uint16_t>(json.getInt("length", 1));
    dataPoint.coefficient = json.getDouble("coefficient", 1.0);
    dataPoint.offset = 0.0; // Default offset
    dataPoint.unit = json.getString("unit");
    dataPoint.description = json.getString("description");

    // Parse data type
    dataPoint.dataType = stringToDataType(json.getString("data_type", "uint16"));

    // Parse function codes to determine register type
    auto functionCodesArray = json.getArray("function_codes");
    if (!functionCodesArray.empty()) {
        int firstFunctionCode = functionCodesArray[0].asInt();
        dataPoint.registerType = getFunctionCodeRegisterType(firstFunctionCode);
    } else {
        dataPoint.registerType = RegisterType::HOLDING_REGISTER; // Default
    }

    // Default byte order (most devices use big endian)
    dataPoint.byteOrder = EByteOrder::BIG;

    // Parse range if provided as string like "0-100"
    std::string rangeStr = json.getString("range");
    if (!rangeStr.empty() && rangeStr != "null") {
        parseRangeString(rangeStr, dataPoint.minValue, dataPoint.maxValue);
    } else {
        dataPoint.minValue = 0.0;
        dataPoint.maxValue = 0.0;
    }

    // Parse bit field if specified
    int bitIndex = json.getInt("bit", -1);
    if (bitIndex >= 0 && bitIndex <= 15) {
        dataPoint.bitField[bitIndex] = dataPoint.chineseName + "_bit" + std::to_string(bitIndex);
    }

    // Default decimal places
    dataPoint.decimalPlaces = 2;

    // Parse new fields from protocol configuration
    dataPoint.section = json.getString("section");
    dataPoint.category = json.getString("category");
    dataPoint.deviceCategory = json.getString("device_category");
    dataPoint.writable = json.getBool("writable", false);

    if (dataPoint.name.empty()) {
        throw std::runtime_error("Data point name cannot be empty");
    }

    return dataPoint;
}

DataType ProtocolManager::stringToDataType(const std::string& str) const {
    if (str == "uint16") return DataType::UINT16;
    if (str == "int16") return DataType::INT16;
    if (str == "uint32") return DataType::UINT32;
    if (str == "int32") return DataType::INT32;
    if (str == "float32") return DataType::FLOAT32;
    if (str == "uint64") return DataType::UINT64;
    if (str == "int64") return DataType::INT64;
    if (str == "float64") return DataType::FLOAT64;
    
    LOG_WARN("Unknown data type: " << str << ", using UINT16");
    return DataType::UINT16;
}

RegisterType ProtocolManager::stringToRegisterType(const std::string& str) const {
    if (str == "holding") return RegisterType::HOLDING_REGISTER;
    if (str == "input") return RegisterType::INPUT_REGISTER;
    if (str == "coil") return RegisterType::COIL;
    if (str == "discrete") return RegisterType::DISCRETE_INPUT;
    
    LOG_WARN("Unknown register type: " << str << ", using HOLDING_REGISTER");
    return RegisterType::HOLDING_REGISTER;
}

EByteOrder ProtocolManager::stringToByteOrder(const std::string& str) const {
    if (str == "big_endian") return EByteOrder::BIG;
    if (str == "little_endian") return EByteOrder::LITTLE;

    LOG_WARN("Unknown byte order: " << str << ", using BIG_ENDIAN");
    return EByteOrder::BIG;
}

RegisterType ProtocolManager::getFunctionCodeRegisterType(int functionCode) const {
    switch (functionCode) {
        case 1: return RegisterType::COIL;
        case 2: return RegisterType::DISCRETE_INPUT;
        case 3: return RegisterType::HOLDING_REGISTER;
        case 4: return RegisterType::INPUT_REGISTER;
        case 5: return RegisterType::COIL;
        case 6: return RegisterType::HOLDING_REGISTER;
        case 15: return RegisterType::COIL;
        case 16: return RegisterType::HOLDING_REGISTER;
        default:
            LOG_WARN("Unknown function code: " << functionCode << ", using HOLDING_REGISTER");
            return RegisterType::HOLDING_REGISTER;
    }
}

void ProtocolManager::parseRangeString(const std::string& rangeStr, double& minValue, double& maxValue) const {
    size_t dashPos = rangeStr.find('-');
    if (dashPos != std::string::npos) {
        try {
            std::string minStr = rangeStr.substr(0, dashPos);
            std::string maxStr = rangeStr.substr(dashPos + 1);
            minValue = std::stod(minStr);
            maxValue = std::stod(maxStr);
        } catch (const std::exception& e) {
            LOG_WARN("Failed to parse range string: " << rangeStr << ", error: " << e.what());
            minValue = 0.0;
            maxValue = 0.0;
        }
    } else {
        minValue = 0.0;
        maxValue = 0.0;
    }
}

const DataPoint* ProtocolManager::getDataPoint(const std::string& name) const {
    auto it = dataPointIndex_.find(name);
    if (it != dataPointIndex_.end()) {
        return &config_.dataPoints[it->second];
    }
    return nullptr;
}

std::vector<std::string> ProtocolManager::getDataPointNames() const {
    std::vector<std::string> names;
    names.reserve(config_.dataPoints.size());
    
    for (const auto& dataPoint : config_.dataPoints) {
        names.push_back(dataPoint.name);
    }
    
    return names;
}

double ProtocolManager::convertRawValue(const std::string& dataPointName, const std::vector<uint16_t>& rawData) const {
    const DataPoint* dataPoint = getDataPoint(dataPointName);
    if (!dataPoint) {
        throw std::runtime_error("Data point not found: " + dataPointName);
    }
    
    double value;
    if (!parseRawData(*dataPoint, rawData, value)) {
        throw std::runtime_error("Failed to parse raw data for: " + dataPointName);
    }
    
    return value;
}

bool ProtocolManager::parseRawData(const DataPoint& dataPoint, const std::vector<uint16_t>& rawData, double& value) const {
    if (rawData.size() < dataPoint.length) {
        LOG_ERROR("Insufficient raw data for data point: " << dataPoint.name);
        return false;
    }
    
    std::vector<uint16_t> pointData(rawData.begin(), rawData.begin() + dataPoint.length);
    
    switch (dataPoint.dataType) {
        case DataType::UINT16:
        case DataType::UINT32:
        case DataType::UINT64:
            value = static_cast<double>(rawDataToUint64(pointData, dataPoint.dataType, dataPoint.byteOrder));
            break;
            
        case DataType::INT16:
        case DataType::INT32:
        case DataType::INT64:
            value = static_cast<double>(rawDataToInt64(pointData, dataPoint.dataType, dataPoint.byteOrder));
            break;
            
        case DataType::FLOAT32:
        case DataType::FLOAT64:
            value = rawDataToFloat(pointData, dataPoint.dataType, dataPoint.byteOrder);
            break;
            
        default:
            LOG_ERROR("Unsupported data type for data point: " << dataPoint.name);
            return false;
    }
    
    value = (value * dataPoint.coefficient) + dataPoint.offset;
    
    if (dataPoint.minValue != dataPoint.maxValue) {
        if (value < dataPoint.minValue || value > dataPoint.maxValue) {
            LOG_WARN("Value out of range for " << dataPoint.name << ": " << value
                     << " (range: " << dataPoint.minValue << " - " << dataPoint.maxValue << ")");
        }
    }
    
    return true;
}

uint64_t ProtocolManager::rawDataToUint64(const std::vector<uint16_t>& rawData, DataType dataType, EByteOrder byteOrder) const {
    uint64_t result = 0;
    
    switch (dataType) {
        case DataType::UINT16:
            result = rawData[0];
            break;
            
        case DataType::UINT32:
            if (rawData.size() >= 2) {
                if (byteOrder == EByteOrder::BIG) {
                    result = (static_cast<uint64_t>(rawData[0]) << 16) | rawData[1];
                } else {
                    result = (static_cast<uint64_t>(rawData[1]) << 16) | rawData[0];
                }
            }
            break;
            
        case DataType::UINT64:
            if (rawData.size() >= 4) {
                if (byteOrder == EByteOrder::BIG) {
                    result = (static_cast<uint64_t>(rawData[0]) << 48) |
                            (static_cast<uint64_t>(rawData[1]) << 32) |
                            (static_cast<uint64_t>(rawData[2]) << 16) |
                            rawData[3];
                } else {
                    result = (static_cast<uint64_t>(rawData[3]) << 48) |
                            (static_cast<uint64_t>(rawData[2]) << 32) |
                            (static_cast<uint64_t>(rawData[1]) << 16) |
                            rawData[0];
                }
            }
            break;
            
        default:
            break;
    }
    
    return result;
}

int64_t ProtocolManager::rawDataToInt64(const std::vector<uint16_t>& rawData, DataType dataType, EByteOrder byteOrder) const {
    uint64_t unsignedValue = rawDataToUint64(rawData, dataType, byteOrder);
    
    switch (dataType) {
        case DataType::INT16:
            return static_cast<int16_t>(unsignedValue);
        case DataType::INT32:
            return static_cast<int32_t>(unsignedValue);
        case DataType::INT64:
            return static_cast<int64_t>(unsignedValue);
        default:
            return static_cast<int64_t>(unsignedValue);
    }
}

double ProtocolManager::rawDataToFloat(const std::vector<uint16_t>& rawData, DataType dataType, EByteOrder byteOrder) const {
    if (dataType == DataType::FLOAT32 && rawData.size() >= 2) {
        uint32_t intValue;
        if (byteOrder == EByteOrder::BIG) {
            intValue = (static_cast<uint32_t>(rawData[0]) << 16) | rawData[1];
        } else {
            intValue = (static_cast<uint32_t>(rawData[1]) << 16) | rawData[0];
        }
        
        float floatValue;
        std::memcpy(&floatValue, &intValue, sizeof(float));
        return static_cast<double>(floatValue);
    }
    
    if (dataType == DataType::FLOAT64 && rawData.size() >= 4) {
        uint64_t intValue;
        if (byteOrder == EByteOrder::BIG) {
            intValue = (static_cast<uint64_t>(rawData[0]) << 48) |
                      (static_cast<uint64_t>(rawData[1]) << 32) |
                      (static_cast<uint64_t>(rawData[2]) << 16) |
                      rawData[3];
        } else {
            intValue = (static_cast<uint64_t>(rawData[3]) << 48) |
                      (static_cast<uint64_t>(rawData[2]) << 32) |
                      (static_cast<uint64_t>(rawData[1]) << 16) |
                      rawData[0];
        }
        
        double doubleValue;
        std::memcpy(&doubleValue, &intValue, sizeof(double));
        return doubleValue;
    }
    
    return 0.0;
}

}  // namespace protocol
}  // namespace ems